<?php

namespace App\Service\Classes;

use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Builder;

use Services\Classes\ArrayBuilder;

class LayuiBuilder
{
    protected $operators = [
        '=', '<', '>', '<=', '>=', '<>', '!=', '<=>',
        'like', 'like binary', 'not like', 'ilike',
        '&', '|', '^', '<<', '>>',
        'rlike', 'regexp', 'not regexp',
        '~', '~*', '!~', '!~*', 'similar to',
        'not similar to', 'not ilike', '~~*', '!~~*',
    ];

    /**
     * ajax请求
     * @var \Illuminate\Http\Request 
     */
    protected $request;

    /**
     * 模型查询器
     * @var \Illuminate\Database\Eloquent\Builder
     */
    protected $query;

    /**
     * 查询构造数组
     * @var Array
     */
    protected $arrayQuery;

    /**
     * Create a new layui query builder instance.
     * @param \Illuminate\Http\Request $request
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param Array $arrayQuery
     * @return void
     */
    public function __construct(
        Request $request,
        Builder $query,
        array $arrayQuery
    ) {
        $this->request    = $request;
        $this->query      = $query;
        $this->arrayQuery = $arrayQuery;

        $this->boot();
    }

    /**
     * Initialize builder
     * @return $this
     */
    protected function boot()
    {
        if (isset($this->arrayQuery['where'])) {
            $this->arrayQuery['where'] = $this->buildWheres($this->arrayQuery['where']);
        }

        //排序
        if ($this->request->has(['orderBy', 'sort'])) {
            $orderBy = $this->request->get('orderBy');
            $sort    = $this->request->get('sort');

            $this->arrayQuery['order'] = "{$orderBy} {$sort}";
        }

        //分页
        if ($this->request->has(['page', 'limit'])) {
            $page  = $this->request->get('page');
            $limit = $this->request->get('limit');

            if (--$page < 0) $page = 0;
            $offset = $page * $limit;

            $this->arrayQuery['offset'] = $offset;
            $this->arrayQuery['limit']  = $limit;
        }

        //日期
        if ($this->request->has(['start_time', 'end_time'])) {
            $startDate = $this->request->get('start_time');
            $endDate   = $this->request->get('end_time');

            $this->arrayQuery['where']['created_at'] = [
                'between' => [
                    $startDate,
                    $endDate,
                ]
            ];
        }

        return $this;
    }

    protected function arrayQuery(array $arrayQuery)
    {
        return (new ArrayBuilder())->apply(clone $this->query, $arrayQuery);
    }

    /**
     * Determine if the given operator is supported.
     *
     * @param  string  $operator
     * @return bool
     */
    protected function invalidOperator($operator)
    {
        return !in_array(strtolower($operator), $this->operators, true);
    }

    /**
     * where查询
     * @param string $key
     * @param string|Array $value
     * @return Array
     */
    protected function buildWhere($key, $value)
    {
        $resultNotNull = function ($input, $data) {
            if (!blank($input)) return $data;
        };

        // ['foo']
        if (is_numeric($key)) {
            $input = $this->request->get($value);
            return $resultNotNull($input, [$value, $input]);
        }

        // ['foo' => 'a']
        // ['foo' => '>=']
        if (is_string($value)) {
            if ($this->invalidOperator($value)) {
                return [$key, $value];
            } else {
                $input = $this->request->get($key);
                return $resultNotNull($input, [$key, [$value => $input]]);
            }
        }
        return $resultNotNull($value, [$key, $value]);
    }

    /**
     * 构建兼容查询
     * @param Array $wheres
     * @return Array
     */
    protected function buildWheres($wheres)
    {
        $result = [];
        foreach ($wheres as $whereField => $where) {
            $whereField = strtolower($whereField);

            if ($whereField === 'or' || $whereField === 'and') {
                $result[$whereField] = $this->buildWheres($where);
                continue;
            }

            if ($data = $this->buildWhere($whereField, $where)) {
                list($column, $value) = $data;
                $result[$column] = $value;
                continue;
            }
        }
        return $result;
    }

    /**
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
     */
    public function query()
    {
        return $this->arrayQuery($this->arrayQuery);
    }

    /**
     * Get the count of the total records for the paginator.
     * @return int
     */
    public function getCountForPagination()
    {
        $arrayQuery = array_except($this->arrayQuery, ['offset', 'limit']);
        return $this->arrayQuery($arrayQuery)->count();
    }

    /**
     * @param  callable|null  $callback
     * @return \Illuminate\Support\Collection
     */
    public function paginate($callback = null)
    {
        $count      = $this->getCountForPagination();
        $collection = $this->arrayQuery($this->arrayQuery)->get();

        if ($callback instanceof \Closure) {
            $collection->map($callback);
        }
        return collect([
            'count' => $count,
            'data'  => $collection,
        ]);
    }
}
